Skip to content

security: fix audit findings across fs sandbox, IPC, grants, plugins, storage#30

Closed
MerhiOPS wants to merge 6 commits intomainfrom
security/audit-fixes
Closed

security: fix audit findings across fs sandbox, IPC, grants, plugins, storage#30
MerhiOPS wants to merge 6 commits intomainfrom
security/audit-fixes

Conversation

@MerhiOPS
Copy link
Contributor

Summary

Comprehensive security audit remediation across 10 files in 3 severity tiers.

HIGH — TOCTOU Filesystem Sandbox Escapes (commit 1)

  • Added post-open path verification (verify_opened_path) to all filesystem operations (read_file, write_file, read_dir, stat, exists, remove, rename, copy)
  • Double symlink check in remove() before remove_dir_all to prevent recursive deletion outside sandbox via symlink swap
  • Narrows the TOCTOU window between safe_resolve() validation and the actual I/O syscall

MEDIUM — IPC, Grants, Plugins, Storage (commit 2)

  • IPC response size limit: 16MB cap prevents memory exhaustion from oversized handler returns
  • JS injection hardening: null byte escape in escape_for_single_quoted_js, __volt_response__/__volt_event__ made non-writable
  • Worker panic safety: catch_unwind around IPC dispatch guarantees in-flight slot release
  • Grant ID unpredictability: replaced timestamp+counter with SHA-256 of process entropy
  • Grant path canonicalization: canonicalize at creation time to prevent symlink drift
  • Plugin dialog sanitization: strip Unicode control/RTL chars, truncate to 100 chars
  • Stderr flooding cap: bounded read at 256KB prevents plugin-driven host OOM
  • Storage quota: 50MB per-plugin limit prevents disk exhaustion
  • Path validation: null byte rejection, all-component reserved name check, whitespace in CSP origin

LOW — Rate Limiting, Channel Reservation, Storage Cleanup (commit 3)

  • Rate limit check moved before native fast path execution (was post-execution)
  • __volt_internal: prefix reserved alongside volt: in handler registration
  • .tmp file cleanup during storage reconciliation
  • Graceful recovery from corrupted _index.json (log + empty index vs hard failure)

Test plan

  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace
  • pnpm typecheck
  • pnpm test
  • Manual: verify safe_resolve rejects .., absolute paths, reserved names in intermediate components
  • Manual: verify IPC response >16MB returns error instead of crashing WebView
  • Manual: verify plugin stderr >256KB doesn't OOM the host
  • Manual: verify storage set past 50MB quota returns error

All filesystem operations now re-verify that the resolved path still
resides within the sandbox boundary after the file is opened/accessed.
This narrows the TOCTOU window between safe_resolve() validation and
the actual I/O syscall, preventing symlink-swap attacks that could
escape the sandbox.

Affected operations: read_file, read_file_text, write_file, read_dir,
stat, exists, remove, rename, copy.

The remove() path additionally double-checks ensure_not_symlink before
remove_dir_all to guard against the most dangerous variant (recursive
deletion outside the sandbox via symlink swap).
…ins, storage

IPC hardening:
- Add IPC_MAX_RESPONSE_BYTES (16MB) cap to prevent memory exhaustion from
  oversized handler return values
- Escape null bytes in JS string injection (escape_for_single_quoted_js)
- Protect __volt_response__ and __volt_event__ with Object.defineProperty
  (writable:false) to prevent interception by injected scripts
- Wrap IPC worker dispatch in catch_unwind to guarantee in-flight slot
  release even if a worker thread panics

Grant system:
- Replace predictable timestamp+counter grant IDs with SHA-256 based IDs
  using process entropy (PID, thread ID, heap ASLR address)
- Canonicalize grant paths at creation time to prevent symlink drift

Plugin security:
- Cap stderr capture at 256KB to prevent plugin-driven host OOM
- Sanitize plugin dialog titles: strip Unicode control characters and
  RTL overrides, truncate to 100 chars to prevent spoofing

Storage:
- Add 50MB per-plugin storage quota to prevent disk exhaustion

Path validation:
- Reject null bytes explicitly in validate_path (defense-in-depth)
- Check ALL path components for reserved device names, not just the last
- Add whitespace rejection to sanitize_dev_server_origin
…storage)

IPC:
- Move rate limit check before native fast path execution so
  rate-limited requests are rejected without performing computation
- Reserve __volt_internal: prefix in addition to volt: to prevent
  user handler squatting on internal channels

Storage:
- Clean up orphaned .tmp files during reconciliation (previously only
  .val files were handled, leaving interrupted atomic writes behind)
- Gracefully recover from corrupted _index.json instead of failing
  all storage operations (log warning, start with empty index)
- Clone response.id before first use to prevent use-after-move (response.rs)
- Replace redundant closure with function reference (clippy)
- Extract path resolution and symlink guards into fs/resolve.rs (152 lines)
- fs/mod.rs now contains file operations only (228 lines)
- Fix test_ipc_init_script_valid: Object.defineProperty changed the
  contiguous string from window.__volt_response__ to '__volt_response__'
@MerhiOPS MerhiOPS closed this Mar 16, 2026
@MerhiOPS MerhiOPS deleted the security/audit-fixes branch March 16, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant